package com.binguo.wxapi.passengerflow;

import com.binguo.common.util.JsonUtil;
import com.binguo.common.util.LoggerUtil;
import com.binguo.model.mapper.ShopAreaMapper;
import com.binguo.model.mapper.ShopAreaPlaceMapper;
import com.binguo.model.mapper.ShopCameraMessageMapper;
import com.binguo.model.mapper.ShopPlaceMapper;
import com.binguo.model.model.ShopArea;
import com.binguo.model.model.ShopAreaPlace;
import com.binguo.model.model.ShopCameraMessage;
import com.binguo.model.model.ShopPlace;
import com.binguo.model.wxapi.vo.ShopPlaceListVO;
import com.binguo.model.wxapi.vo.ShopPlaceVO;
import com.binguo.wxapi.passengerflow.hkym.*;
import com.binguo.wxapi.service.RedisLockService;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Condition;

import javax.annotation.Resource;

import java.util.*;
import java.util.stream.Collectors;

import static com.binguo.wxapi.passengerflow.PassengerFlowConstant.*;

@Service
public class PassengerFlowService implements InitializingBean {

    private static Logger logger = LoggerFactory.getLogger(PassengerFlowService.class);

    @Resource
    private ShopPlaceMapper shopPlaceMapper;

    @Resource
    private ShopCameraMessageMapper shopCameraMessageMapper;

    @Resource
    private ShopAreaMapper shopAreaMapper;

    @Resource
    private ShopAreaPlaceMapper shopAreaPlaceMapper;

    @Resource
    RedisLockService redisLockService;

    @Autowired
    YmApiFeign ymApiFeign;


    private List<String> startedDeviceSerialList = null;

    public ShopPlaceListVO getStatisticData(Integer shopId) {
        Condition shopAreaCondition = new Condition(ShopArea.class);
        shopAreaCondition.or().andEqualTo("shopId", shopId)
                            .andEqualTo("status", 1);
        List<ShopArea> shopAreaList = shopAreaMapper.selectByCondition(shopAreaCondition);
        if (shopAreaList != null && shopAreaList.size() > 0) {
            List<Integer> areaIdList = shopAreaList.stream().map(ShopArea::getId).collect(Collectors.toList());

            Condition shopAreaPlaceCondition = new Condition(ShopAreaPlace.class);
            shopAreaPlaceCondition.or().andIn("areaId", areaIdList);
            List<ShopAreaPlace> shopAreaPlaceList = shopAreaPlaceMapper.selectByCondition(shopAreaPlaceCondition);

            final Map<Integer, ShopPlace> shopPlaceMap;
            final Map<Integer, List<ShopAreaPlace>> shopAreaPlaceIdMap;
            if (shopAreaPlaceList != null && shopAreaPlaceList.size() > 0) {
                shopAreaPlaceIdMap = new HashMap<>();
                List<Integer> placeIdList = new ArrayList<>();
                for (ShopAreaPlace shopAreaPlace : shopAreaPlaceList) {
                    placeIdList.add(shopAreaPlace.getPlaceId());

                    List<ShopAreaPlace> shopPlaceIdList = shopAreaPlaceIdMap.get(shopAreaPlace.getAreaId());
                    if (shopPlaceIdList == null) {
                        shopPlaceIdList = new ArrayList<>();
                    }

                    shopPlaceIdList.add(shopAreaPlace);

                    shopAreaPlaceIdMap.put(shopAreaPlace.getAreaId(), shopPlaceIdList);
                }

                String placeIdListStr = org.apache.commons.lang3.StringUtils.join(placeIdList.toArray(), ",");
                List<ShopPlace> shopPlaceList = shopPlaceMapper.selectByIds(placeIdListStr);

                shopPlaceMap =
                        shopPlaceList.stream().collect(Collectors.toMap(ShopPlace::getId, s -> s));

            }else {
                shopAreaPlaceIdMap = null;
                shopPlaceMap = null;
            }

            ShopPlaceListVO shopPlaceListVO = new ShopPlaceListVO();
            shopPlaceListVO.setPlaceList(new ArrayList<>());

            shopAreaList.stream().forEach(shopArea -> {
                ShopPlaceVO shopPlaceVO = new ShopPlaceVO();
                shopPlaceVO.setId(shopArea.getId());
                shopPlaceVO.setName(shopArea.getAreaName());
                shopPlaceVO.setC(0);
                shopPlaceVO.setT(shopArea.getMostPeopleNum().intValue());

                if (shopAreaPlaceIdMap != null) {
                    List<ShopAreaPlace> shopPlaceIdList = shopAreaPlaceIdMap.get(shopArea.getId());
                    Integer in = 0;
                    Integer out = 0;
                    for (ShopAreaPlace shopAreaPlace : shopPlaceIdList) {
                        ShopPlace shopPlace = shopPlaceMap.get(shopAreaPlace.getPlaceId());
                        if (shopAreaPlace.getType() == null || shopAreaPlace.getType() == 1) {
                            in += shopPlace.getInCount();
                            out += shopPlace.getOutCount();
                        }else {
                            in += shopPlace.getOutCount();
                            out += shopPlace.getInCount();
                        }
                    }

                    shopPlaceVO.setC(in - out);
                }

                shopPlaceListVO.getPlaceList().add(shopPlaceVO);
            });

            return shopPlaceListVO;
        }

        return null;
    }

    @Scheduled(cron="0/30 * * * * ?") //每分钟执行更新
    public void handleHikMessage() throws Exception {
        LoggerUtil.console.info("====handleHikMessage===");
        String key = "handleHikMessage";

        String uuid = UUID.randomUUID().toString();
        boolean lock = redisLockService.setLock(key, 20 * 60 * 1000, uuid);
        if (lock) {
            syncMessage();
            redisLockService.releaseLock(key, uuid);
        }
    }



    @Scheduled(cron="0 0 * * * ?") //每天零晨更新token
    public void getToken() throws Exception {
        LoggerUtil.console.info("====getToken===");
        String key = "getToken";

        String uuid = UUID.randomUUID().toString();
        boolean lock = redisLockService.setLock(key, 2 * 60 * 1000, uuid);
        if (lock) {
            String token = getTokenHandler();
            if (token != null) {
                redisLockService.setKey(KEY_TOKEN, token, 7*24*60*1000);
            }

            zeroCameraCounting();
        }
    }



    public void getAllStartedDeviceList(){
        Condition shopPlaceCondition = new Condition(ShopPlace.class);
        shopPlaceCondition.or().andEqualTo("startState", FLAG_STARTED);
        List<ShopPlace> shopPlaceList = shopPlaceMapper.selectByCondition(shopPlaceCondition);
        if (shopPlaceList != null && shopPlaceList.size() > 0) {
            startedDeviceSerialList = shopPlaceList.stream().map(ShopPlace::getDeviceSerial).collect(Collectors.toList());
        }
    }

    public void syncMessage(){
        try {
            TOKEN = redisLockService.get(KEY_TOKEN);
            MESSAGE_ID = redisLockService.get(KEY_MESSAGE_ID);

            if (TOKEN == null) {
                TOKEN = getTokenHandler();
                if (TOKEN != null) {
                    redisLockService.setKey(KEY_TOKEN, TOKEN, 7*24*60*1000);
                }else {
                    return;
                }
            }
            if (MESSAGE_ID == null) {
                CommonObj<ConsumerId> consumerIdCommonObj = ymApiFeign.consumer(TOKEN, CONSUME_NAME);
                MESSAGE_ID = consumerIdCommonObj.getData().getConsumerId();
                if (MESSAGE_ID != null) {
                    redisLockService.setKey(KEY_MESSAGE_ID, MESSAGE_ID, 7*24*60*1000);
                }else {
                    return;
                }
            }
            CommonObj listCommonObj
                    = ymApiFeign.messages(TOKEN, MESSAGE_ID, true);

            if (listCommonObj != null && listCommonObj.getCode() != 200) {
                if (listCommonObj.getCode() == 514002) {//{"code":514002,"message":"无效的consumerId","success":false,"data":null}
                    CommonObj<ConsumerId> consumerIdCommonObj = ymApiFeign.consumer(TOKEN, CONSUME_NAME);
                    MESSAGE_ID = consumerIdCommonObj.getData().getConsumerId();
                    listCommonObj
                            = ymApiFeign.messages(TOKEN, MESSAGE_ID, true);

                    redisLockService.setKey(KEY_MESSAGE_ID, MESSAGE_ID, 7*24*60*1000);
                }
            }

            if (listCommonObj != null && listCommonObj.getCode() != 200) {
                return;
            }

            String result = null;
            result = JsonUtil.toJson(listCommonObj);
            logger.info("hik message={}",result);


            if (listCommonObj != null && listCommonObj.getData() != null) {
                List<ConsumeMsg> consumeMsgList = (List<ConsumeMsg>)listCommonObj.getData();
                if (consumeMsgList.size() > 0) {
                    try {
                        List<String> deviceSerialList = new ArrayList<>();

                        Map<String, Integer> addedDeviceEnterNumMap = new HashMap<>();
                        Map<String, Integer> addedDeviceOutNumMap = new HashMap<>();
    //                    List<String> deviceSerialList = consumeMsgList.stream().map(xx::xxx).collect(Collectors.toList());
                        List<ShopCameraMessage> messageList = new ArrayList<>();
                        for (ConsumeMsg consumeMsg : consumeMsgList) {
                            if (consumeMsg.getMsgType() != null) {
                                if (MESSAGE_TYPE_FLOW.equals(consumeMsg.getMsgType())) {

                                    MessageContent messageContent = JsonUtil.parse(consumeMsg.getContent(), MessageContent.class);

                                    if (startedDeviceSerialList != null && startedDeviceSerialList.size() > 0 && startedDeviceSerialList.contains(messageContent.getDeviceSerial())) {
                                        logger.info("message content==={}", consumeMsg.getContent());
                                    }else {
                                        //未开启计数时停止获取消息
                                        continue;
                                    }

                                    Object key = redisLockService.get(consumeMsg.getMsgId());
                                    if (key != null) {
                                        continue;
                                    }
                                    redisLockService.setKey(consumeMsg.getMsgId(), "1", 12 * 60 * 1000);

                                    ShopCameraMessage shopCameraMessage = new ShopCameraMessage();
                                    shopCameraMessage.setDeviceSerial(messageContent.getDeviceSerial());
                                    shopCameraMessage.setCreateTime(new Date());
                                    shopCameraMessage.setLeaveNum(Integer.valueOf(messageContent.getLeaveNum()));
                                    shopCameraMessage.setEnterNum(Integer.valueOf(messageContent.getEnterNum()));

                                    shopCameraMessage.setMessageId(consumeMsg.getMsgId());
                                    shopCameraMessage.setEndTime(SIMPLEFORMATTER.parse(messageContent.getEndTime()));
                                    shopCameraMessage.setStartTime(SIMPLEFORMATTER.parse(messageContent.getStartTime()));
                                    shopCameraMessage.setMessageTime(SIMPLEFORMATTER.parse(messageContent.getMessageTime()));

                                    Integer enterNum = addedDeviceEnterNumMap.get(messageContent);
                                    if (enterNum == null) {
                                        enterNum = 0;
                                    }
                                    addedDeviceEnterNumMap.put(messageContent.getDeviceSerial(), enterNum + Integer.valueOf(messageContent.getEnterNum()));

                                    Integer outNum = addedDeviceOutNumMap.get(messageContent);
                                    if (outNum == null) {
                                        outNum = 0;
                                    }
                                    addedDeviceOutNumMap.put(messageContent.getDeviceSerial(), outNum + Integer.valueOf(messageContent.getLeaveNum()));

                                    messageList.add(shopCameraMessage);
                                    deviceSerialList.add(messageContent.getDeviceSerial());
                                }
                            }
                        }

                        if (deviceSerialList.size() > 0) {
                            Condition shopPlaceCondition = new Condition(ShopPlace.class);
                            shopPlaceCondition.or().andIn("deviceSerial", deviceSerialList);
                            List<ShopPlace> shopPlaceList = shopPlaceMapper.selectByCondition(shopPlaceCondition);
                            if (shopPlaceList != null && shopPlaceList.size() > 0) {
                                Map<String, Integer> deviceInCountMap = shopPlaceList.stream().collect(Collectors.toMap(ShopPlace::getDeviceSerial, ShopPlace::getInCount));
                                Map<String, Integer> deviceOutCountMap = shopPlaceList.stream().collect(Collectors.toMap(ShopPlace::getDeviceSerial, ShopPlace::getOutCount));
                                Map<String, Integer> devicePlaceIdMap = shopPlaceList.stream().collect(Collectors.toMap(ShopPlace::getDeviceSerial, ShopPlace::getId));

                                messageList.stream().forEach(message -> {
                                    String deviceSerial = message.getDeviceSerial();
                                    Integer dbInCount = deviceInCountMap.get(deviceSerial);
                                    Integer dbOutCount = deviceOutCountMap.get(deviceSerial);

                                    Integer addedInCount = addedDeviceEnterNumMap.get(deviceSerial);
                                    Integer addedOutCount = addedDeviceOutNumMap.get(deviceSerial);

                                    Integer in = dbInCount + addedInCount;
                                    Integer out = dbOutCount + addedOutCount;

                                    message.setInCount(in);
                                    message.setOutCount(out);
                                    message.setShopPlaceId(devicePlaceIdMap.get(deviceSerial));

                                });

                                shopPlaceList.stream().forEach(shopPlace -> {
                                    String deviceSerial = shopPlace.getDeviceSerial();
                                    Integer dbInCount = deviceInCountMap.get(deviceSerial);
                                    Integer dbOutCount = deviceOutCountMap.get(deviceSerial);

                                    Integer addedInCount = addedDeviceEnterNumMap.get(deviceSerial);
                                    Integer addedOutCount = addedDeviceOutNumMap.get(deviceSerial);

                                    Integer in = dbInCount + addedInCount;
                                    Integer out = dbOutCount + addedOutCount;

                                    if (in > 0 && out > 0) {
                                        ShopPlace temp = new ShopPlace();
                                        temp.setId(shopPlace.getId());
                                        temp.setInCount(in);
                                        temp.setOutCount(out);
                                        temp.setUpdateTime(new Date());
                                        temp.setCurNumber(in - out);
                                        if (shopPlace.getTotal() != null && shopPlace.getTotal() > 0) {
                                            temp.setRemain(shopPlace.getTotal() - temp.getCurNumber());
                                        }
                                        shopPlaceMapper.updateByPrimaryKeySelective(temp);
                                    }
                                });
                            }
                        }

                        if (messageList.size() > 0) {
                            shopCameraMessageMapper.insertList(messageList);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    @Override
    public void afterPropertiesSet() throws Exception {
        getAllStartedDeviceList();
        getToken();
    }


    private String getTokenHandler(){
        TokenResponse tokenResponse = ymApiFeign.getToken(CLIENTID, CLIENT_SECRET, CLIENT_CREDENTIALS);
        if (tokenResponse != null) {
            return "bearer " + tokenResponse.getAccess_token();
        }
        return null;
    }

    private void zeroCameraCounting(){
        ShopPlace shopPlace = new ShopPlace();
        shopPlace.setInCount(0);
        shopPlace.setOutCount(0);
        shopPlace.setUpdateTime(new Date());

        Condition shopPlaceCondition = new Condition(ShopPlace.class);
        shopPlaceCondition.or().andGreaterThan("id", 0);
        shopPlaceMapper.updateByConditionSelective(shopPlace, shopPlaceCondition);
    }
}
